home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Carousel
/
CAROUSEL.cdr
/
mactosh
/
utilprn
/
hpdeskje.sit
/
HPDJet ƒ
/
PDEF5.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-04-02
|
21KB
|
705 lines
/* 02.04.1989 amn (latest edit) */
/* PDEF5.c - printer driver for Macintosh and HP DeskJet, spool file printing. */
/* Compiles into 'PDEF' resource, id 5, name ''. */
/* We cannot use any global variables in this code. However, the globals of the */
/* low-level driver (XPrint) are available thru the handle 'dCtlStorage' in the */
/* driver's device control entry. */
/* This resource is placed into the printer resource file */
/* 'HP DeskJet', type 'PRER', creator 's89^' as */
/* 'PDEF' 5 '' by 'PRER_Builder' utility program. This utility adds a small jump */
/* table in front of the code produced by LightspeedC. */
/* This procedure handles spool file printing... */
/* Authors: Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
/* Copyright Ari Mujunen, Olli Arnberg 1989. */
/* You may redistribute the driver (=printer resource file, source files, */
/* documentation file(s), and the file 'Copyright and Source Offer') */
/* only _non-commercially_ and _in its entirety_. */
/* See the file 'Copyright and Source Offer' and/or documentation for details. */
/* Acknowledgements: Special thanks to Mr. Earle R. Horton for his 'Daisy' */
/* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
/* This driver served as a basis and inspiration for our work. It also */
/* proofed that a Macintosh printer driver can be done despite the lack of */
/* documentation from Apple. */
/* Change history: */
/* Version When Who Why */
/* 2.0 09.12.1988 amn Original rewrite. */
/* 11.12.1988 amn Cleaning up. */
/* 12.12.1988 amn HP DeskJet's physical margins taken into account. */
/* 15.12.1988 amn Deletion of print file. Banding buffer cleared */
/* before each band. */
/* 07.03.1989 amn,oar Copies-loop. */
/* 10.03.1989 amn Re-microspacing of text by spExtra. */
/* 11.03.1989 amn Re-microspacing of text char by char. */
/* 24.03.1989 amn Long strings (>= 2040 pixels) are chopped. */
/* 26.03.1989 amn GrafPort is preserved across idle proc call. */
/* 26.03.1989 amn Settable printer origin. */
/* 31.03.1989 amn If we are making a permanent spool file, */
/* do not print anything. */
/* Updating TPrStatus output parameter. */
/* 2.1 02.04.1989 amn,oar Released version. */
#include "common_mac_includes.h"
/* Mac OS includes specific to this module: */
/* None yet. */
#include "prglobals.h"
#include "procedures_for_PDEF5.c"
#define BANDBITMAPBUFFERSIZE 30000
/* Function prototypes. */
/* Procedure dispatcher. */
void main(int);
/* Opens and prints spool file. */
pascal void myPrPicFile(THPrint, TPPrPort, Ptr, Ptr, TPrStatus *);
/* Prints the whole spool file by calling 'drawOnePage' for pages found in spool file. */
void drawSpoolFile(int, TPPrPort, Ptr, TPrStatus *);
/* Repeatedly draws the picture of a given page to a partial bit map called 'band'. */
/* Once a band is drawn, sends it to the printer using XPrint. */
void drawOnePage(int, long, TPPrPort, Ptr, TPrStatus *);
/* Replaces the standard QuickDraw low-level procedure for drawing text. */
pascal void myStdText(int, Ptr, Point, Point);
/* Draws string in parts, the length of which is less than 2000 pixels. */
pascal void stdTextInParts(int, int, Ptr, Point, Point, Ptr);
/* Used to call standard QD text-drawing/measuring procedure. We use prototypes... */
pascal void CallPascal(int, Ptr, Point, Point, Ptr);
/* Processes "string original length" picture comments. */
pascal void myStdComment(int, int, htApplicationComment);
/* Gets picture data for QuickDraw directly from spool file. */
pascal void getPICTData(Ptr, int);
/* Function definitions. */
void
main(routineSelector)
int routineSelector;
{
/* The jump table code inserted before our code resource by 'PRER_Builder' */
/* utility program pushes a word onto stack indicating which routine is called. */
/* We pop it off the stack and select an appropriate routine. */
switch(routineSelector) {
case 0:
asm {
unlk a6 ;; LSC generates an 'link' instruction to access parameters
move.l (a7)+, d0 ;; pop return address to our jump table code (discarded)
move.w (a7)+, d0 ;; pop argument 'routineSelector'
jmp myPrPicFile
}
} /* switch */
/* We should not arrive here; Printing Manager has called a non-existent routine. */
SysError(5); /* check bounds trap ??? */
} /* main */
pascal void
myPrPicFile(hPrint, pPrPort, pIOBuf, pDevBuf, prStatus)
THPrint hPrint;
TPPrPort pPrPort;
Ptr pIOBuf, pDevBuf;
TPrStatus *prStatus;
{
TPPrPort thisPort;
Ptr bandBuffer;
ptXprintGlobals xPrintGlobals;
OSErr retCode;
{ /* Lock this code resource in memory (Printing Manager should do this, but...). */
Handle us;
us = (Handle)GetResource('PDEF', 5);
if (us == nil) {
PrintErr = ResError();
return;
}
HLock(us);
}
xPrintGlobals = GET_XPRINT_GLOBALS;
/* If we are making a permanent spool file, do not print now. */
if (xPrintGlobals->spoolFileIsNamedAndPermanent)
return;
if (pPrPort == nil) { /* printing GrafPort NOT preallocated */
if ((thisPort = (TPPrPort)NewPtr((long)sizeof(TPrPort))) == nil) {
PrintErr = iMemFullErr;
return;
}
thisPort->fOurPtr = TRUE;
}
else { /* printing GrafPort already preallocated */
thisPort = pPrPort;
thisPort->fOurPtr = FALSE;
}
prStatus->pPrPort = thisPort; /* let the application see the pointer to printing port, too */
prStatus->hPrint = hPrint; /* why on earth we should return the handle application gave us? */
prStatus->fPgDirty = TRUE; /* always... ??? */
prStatus->fImaging = TRUE;
OpenPort(thisPort); /* this could cause memory manager error ??? */
if ((bandBuffer = NewPtr((long)(BANDBITMAPBUFFERSIZE))) == nil) {
retCode = iMemFullErr;
goto cleanUp;
}
if (retCode = FSOpen(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol,
&xPrintGlobals->spoolFileRefNum) != noErr) /* getPICTData needs this global refnum */
goto disposAndCleanUp;
/* If we had our own idle proc, it is now in different address. */
if (!xPrintGlobals->applicationOwnsIdleProc)
xPrintGlobals->printRecord.prJob.pIdleProc = (ProcPtr)checkForCommandPeriod;
prStatus->iTotCopies = xPrintGlobals->printRecord.prJob.iCopies;
for (prStatus->iCurCopy=0;
prStatus->iCurCopy < prStatus->iTotCopies;
prStatus->iCurCopy++) {
drawSpoolFile(xPrintGlobals->spoolFileRefNum, thisPort, bandBuffer, prStatus);
}
(void)FSClose(xPrintGlobals->spoolFileRefNum);
(void)FSDelete(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol
);
DisposPtr(bandBuffer);
retCode = noErr;
cleanUp:
PrintErr = retCode;
ClosePort(thisPort);
if (thisPort->fOurPtr)
DisposPtr(thisPort);
return;
disposAndCleanUp:
DisposPtr(bandBuffer);
goto cleanUp;
} /* myPrPicFile */
void
drawSpoolFile(spoolFileRefNum, thisPort, bandBuffer, prStatus)
int spoolFileRefNum;
TPPrPort thisPort;
Ptr bandBuffer;
TPrStatus *prStatus;
{
THPfHeader hHeader;
long count;
int i;
OSErr retCode;
hHeader = (THPfHeader)NewHandle((long)sizeof(TPfHeader));
if (hHeader == nil)
return;
retCode = SetFPos(spoolFileRefNum, fsFromStart, 0L);
if (retCode != noErr) {
DisposHandle(hHeader);
return;
}
count = sizeof(TPfHeader);
HLock(hHeader);
retCode = FSRead(spoolFileRefNum, &count, *hHeader);
HUnlock(hHeader);
if (retCode != noErr) {
DisposHandle(hHeader);
return;
}
prStatus->iTotPages = iPfMaxPgs; /* hmm, _max_ (=128) ??? */
for (i=1; i<=prStatus->iTotPages; i++)
if ( (*hHeader)->pfPgDir.lPgPos[i] > 0 ) {
prStatus->iCurPage = i;
drawOnePage(
spoolFileRefNum,
(*hHeader)->pfPgDir.lPgPos[i],
thisPort,
bandBuffer,
prStatus
);
if (PrintErr != noErr)
goto cleanUp;
}
cleanUp:
DisposHandle(hHeader);
} /* drawSpoolFile */
void
drawOnePage(spoolFileRefNum, offset, thisPort, bandBuffer, prStatus)
int spoolFileRefNum;
long offset;
TPPrPort thisPort;
Ptr bandBuffer;
TPrStatus *prStatus;
{
ptXprintGlobals xPrintGlobals;
ptPrParam pXPrintParameterBlock;
OSErr retCode;
Rect pageRect,
bigRect;
QDProcs qdp;
int resolution;
int bandHeight, bandRowBytes, i;
Point marginOffset;
xPrintGlobals = GET_XPRINT_GLOBALS;
pXPrintParameterBlock = &xPrintGlobals->xPrintParameterBlock;
if ((xPrintGlobals->printRecord.prStl.feed == feedCut)
&& (waitNextPage() == FALSE)) { /* Dialog 'Insert next sheet...' */
PrintErr = iPrAbort;
return;
}
if ((prStatus->hPic = (PicHandle)NewHandle((long)sizeof(Picture))) == nil) {
PrintErr = iMemFullErr;
return;
}
{ /* Read picture header (page rectangle (=drawing area) is in the picture frame. */
long count;
if ((retCode = SetFPos(spoolFileRefNum, fsFromStart, offset)) != noErr)
goto cleanUp;
count = sizeof(Picture);
HLock(prStatus->hPic);
retCode = FSRead(spoolFileRefNum, &count, *(prStatus->hPic));
HUnlock(prStatus->hPic);
if (retCode != noErr)
goto cleanUp;
}
/* This is the drawing area application drawed into: */
/* pageRect = (**(prStatus->hPic)).picFrame; */
/* ...but we are going to use page rectangle, since application might have wanted */
/* to scale its drawing into the page rectangle. */
pageRect = xPrintGlobals->printRecord.prInfo.rPage;
{ /* Calculate target bitmap dimensions. */
Rect tr1, tr2;
tr1.top = 0;
tr1.left = 0;
tr1.bottom = 100;
tr1.right = 100;
tr2.top = 0;
tr2.left = 0;
tr2.bottom = tr2.right = 4*xPrintGlobals->printRecord.printX[7];
resolution = (xPrintGlobals->printRecord.prStl.wDev & RES_MASK) + 1; /* should be 1=300, 2=150, 3=100, 4=75 */
tr2.right /= resolution;
tr2.bottom /= resolution;
/* if "Exact dimensions" are required, we should multiply by 1.04 (=75/72) */
bigRect = pageRect;
MapRect(&bigRect, &tr1, &tr2); /* This is the (virtual) bitmap area we are going to play the recorded picture. */
}
/* Calculate the vertical difference between DeskJet's physical left margin (1/8") */
/* and requested left margin (-rPaper.left) in printer pixels. */
marginOffset.v = -(xPrintGlobals->printRecord.rPaper.left
+ xPrintGlobals->currentSettings.printerOrigin.h) * 4 / resolution;
marginOffset.h = -(xPrintGlobals->printRecord.rPaper.top
+ xPrintGlobals->currentSettings.printerOrigin.v) * 4 / resolution;
/* Calculate the bandHeight from resolution and available memory for bit map. */
bandRowBytes = ((bigRect.right - bigRect.left + marginOffset.v) / 16 + 1) * 2; /* QD wants even number of bytes */
bandHeight = BANDBITMAPBUFFERSIZE / bandRowBytes;
InitPort(thisPort); /* current and fresh */
/* GrafDevice(?) ??? */
{ /* Set the printing grafport to draw into offscreen bit image 'bandBuffer'. */
BitMap bm;
bm.baseAddr = bandBuffer;
bm.rowBytes = bandRowBytes;
bm.bounds.top = 0;
bm.bounds.left = 0;
bm.bounds.bottom = bandHeight;
bm.bounds.right = bandRowBytes*8;
SetPortBits(&bm);
}
/* Set the port rectangle to the virtual bitmap rectangle (=enlarged page). */
thisPort->gPort.portRect = bigRect;
/* visRgn should be what ??? */
RectRgn(
thisPort->gPort.visRgn,
&thisPort->gPort.portRect
);
SetStdProcs(&qdp);
qdp.textProc = (QDPtr)myStdText;
qdp.commentProc = (QDPtr)myStdComment;
qdp.getPicProc = (QDPtr)getPICTData;
thisPort->gPort.grafProcs = (QDProcsPtr) &qdp; /* actually we have the procs in the PRINTING GrafPort */
/* Calculate total number of bands. */
prStatus->iTotBands = (bigRect.bottom - bigRect.top) / bandHeight;
for (i=0; i <= prStatus->iTotBands; i++) {
Rect tempClip;
long count;
prStatus->iCurBand = i;
MovePortTo(marginOffset.v, -(bandHeight * i) + marginOffset.h);
/* Because XPrint currently cannot limit printing to BitMap's 'bounds' rectangle, */
/* we clear it to prevent unused margin bits which are garbage. */
/* Note that we cannot use 'EraseRect()' because it draws only within port's */
/* clip&visRgns and portRect and portBit.bounds. */
{
Ptr p;
for(p=bandBuffer+BANDBITMAPBUFFERSIZE-1; p>=bandBuffer; p--)
*p = (unsigned char)0;
}
/*** tempClip.top = bandHeight * i - marginOffset.h;
tempClip.left = bigRect.left;
if (i==prStatus->iTotBands)
tempClip.bottom = tempClip.top + (bigRect.bottom - bigRect.top) % bandHeight;
else
tempClip.bottom = tempClip.top + bandHeight;
tempClip.bottom -= marginOffset.h;
tempClip.right = bigRect.left + bandRowBytes*8; / * actually bigRect.right is ok ??? * /
ClipRect(&tempClip);
EraseRect(&tempClip); ***/
count = offset + (long)sizeof(Picture);
if ((retCode = SetFPos(spoolFileRefNum, fsFromStart, count)) != noErr)
goto cleanUp;
DrawPicture(prStatus->hPic, &bigRect);
/* Run the background procedure before every band. */
{
GrafPtr savePort;
GetPort(&savePort);
(*(xPrintGlobals->printRecord.prJob.pIdleProc))();
SetPort(savePort);
}
if (PrintErr == iPrAbort) {
retCode = PrintErr;
goto cleanUp;
}
/* Send band bit map to low-level driver. */
pXPrintParameterBlock->csCode = iPrBitsCtl;
pXPrintParameterBlock->lParam1 = (long)(&(thisPort->gPort.portBits));
if (i==prStatus->iTotBands)
thisPort->gPort.portBits.bounds.bottom =
thisPort->gPort.portBits.bounds.top
+ (bigRect.bottom - bigRect.top) % bandHeight;
pXPrintParameterBlock->lParam2 = (long)(&(thisPort->gPort.portBits.bounds));
pXPrintParameterBlock->lParam3 = (long)(300 / resolution);
if ((retCode = PBControl(pXPrintParameterBlock, FALSE)) != noErr)
goto cleanUp;
/* We cannot accept command-. -interrupt during bit map printing. */
}
/* Form feed: */
pXPrintParameterBlock->csCode = iPrDevCtl;
pXPrintParameterBlock->lParam1 = lPrPageEnd;
if ((retCode = PBControl(pXPrintParameterBlock, FALSE)) != noErr)
goto cleanUp;
retCode = noErr;
cleanUp:
PrintErr = retCode;
thisPort->gPort.grafProcs = nil;
DisposHandle(prStatus->hPic);
return;
} /* drawOnePage */
pascal void
myStdText(byteCount, textBuffer, numer, denom)
int byteCount;
Ptr textBuffer;
Point numer, denom;
{
ptXprintGlobals xPrintGlobals;
QDProcs stdProcs;
int lengthByQuickDraw;
Fixed intermediateLength;
int lengthByQuickDrawAfterScaling;
Point measuringNumer;
Point measuringDenom;
FontInfo measuringInsistOnReturningThis;
int lengthDifference;
int i;
int numberOfBlanks;
xPrintGlobals = GET_XPRINT_GLOBALS;
SetStdProcs(&stdProcs);
/* Call standard text measurement routine. */
/* Would use CallPascal, but this is a function and returns int. */
measuringNumer = numer;
measuringDenom = denom;
asm {
subq.l #2, a7 ;; Room for function return
move.w byteCount, -(a7)
move.l textBuffer, -(a7)
pea measuringNumer
pea measuringDenom
pea measuringInsistOnReturningThis
_StdTxMeas
move.w (a7)+, lengthByQuickDraw
}
lengthDifference =
FixRound(
FixMul(
FixRatio(xPrintGlobals->originalStringLength, 1),
FixRatio(numer.h, denom.h)
) -
(intermediateLength = FixMul(
FixRatio(lengthByQuickDraw, 1),
FixRatio(measuringNumer.h, measuringDenom.h)
))
);
lengthByQuickDrawAfterScaling = FixRound(intermediateLength);
/* If difference is insignificant, draw text using standard procedure. */
if ((lengthDifference >= -1) && (lengthDifference <= 1)) {
stdTextInParts(
lengthByQuickDrawAfterScaling,
byteCount,
textBuffer, numer, denom, stdProcs.textProc
);
return;
}
/* Count blanks and adjust spExtra if space characters do not */
/* shrink to less than half or widen to more than double width. */
numberOfBlanks = 0;
for (i=0;i<byteCount;i++)
if (textBuffer[i]==' ')
numberOfBlanks++;
if (numberOfBlanks > 0) { /* if there are spaces to be exploited... */
unsigned char blank;
int lengthOfSpaceCharacterInPixels;
int absoluteValueOfLengthDifferenceToleratedBySpacesInTheString;
/* Measure the width of a space character. */
/* Would use CallPascal, but this is a function and returns int. */
blank = ' ';
measuringNumer = numer;
measuringDenom = denom;
asm {
subq.l #2, a7 ;; Room for function return
move.w #1, -(a7)
pea blank
pea measuringNumer
pea measuringDenom
pea measuringInsistOnReturningThis
_StdTxMeas
move.w (a7)+, lengthOfSpaceCharacterInPixels
}
absoluteValueOfLengthDifferenceToleratedBySpacesInTheString =
/* lengthOfSpaceCharacterInPixels * numberOfBlanks / 2 */
FixRound(
FixMul(
FixRatio(lengthOfSpaceCharacterInPixels * numberOfBlanks, 2),
FixRatio(measuringNumer.h, measuringDenom.h)
)
);
/* If spaces can absorb the length difference, we use them. */
if ((lengthDifference >= - absoluteValueOfLengthDifferenceToleratedBySpacesInTheString)
&& (lengthDifference <= absoluteValueOfLengthDifferenceToleratedBySpacesInTheString)) {
GrafPtr gp;
Fixed savedSpExtra;
GetPort(&gp);
savedSpExtra = gp->spExtra;
SpaceExtra(savedSpExtra +
FixMul(
FixRatio(lengthDifference, numberOfBlanks),
FixRatio(denom.h, numer.h)
)
);
stdTextInParts(
lengthByQuickDrawAfterScaling,
byteCount,
textBuffer, numer, denom, stdProcs.textProc
);
SpaceExtra(savedSpExtra);
return;
} /* if spaces are used */
} /* if there was at least one space */
/* Otherwise we must used the brute force method: draw char by char. */
/* First we recalculate the length of the string in pixels */
/* _character by character_. This is because we are going to draw the string */
/* in that way and round-off errors will occur while QD positions the pen. */
lengthByQuickDraw = 0;
for (i=0;i<byteCount;i++) {
Ptr p;
int length;
p = &(textBuffer[i]);
measuringNumer = numer;
measuringDenom = denom;
asm {
subq.l #2, a7 ;; Room for function return
move.w #1, -(a7)
move.l p, -(a7)
pea measuringNumer
pea measuringDenom
pea measuringInsistOnReturningThis
_StdTxMeas
move.w (a7)+, length ;; here it rounds off
}
lengthByQuickDraw += length;
} /* for */
lengthDifference =
FixRound(
FixMul(
FixRatio(xPrintGlobals->originalStringLength, 1),
FixRatio(numer.h, denom.h)
) -
(intermediateLength = FixMul(
FixRatio(lengthByQuickDraw, 1),
FixRatio(measuringNumer.h, measuringDenom.h)
))
);
lengthByQuickDrawAfterScaling = FixRound(intermediateLength);
{
int numberOfInterCharacterSlots;
int cumulative;
int pixelsToOffsetForThisCharacter;
numberOfInterCharacterSlots = byteCount - 1;
cumulative = 0;
for (i=0;i<numberOfInterCharacterSlots;i++) {
CallPascal(1, &(textBuffer[i]), numer, denom, stdProcs.textProc); /* draw one char */
cumulative += lengthDifference;
if ((pixelsToOffsetForThisCharacter = cumulative / numberOfInterCharacterSlots) != 0) {
Move(pixelsToOffsetForThisCharacter, 0);
cumulative -= (pixelsToOffsetForThisCharacter * numberOfInterCharacterSlots);
}; /* if we are moving pen by ourselves now */
} /* for */
CallPascal(1, &(textBuffer[numberOfInterCharacterSlots]), numer, denom, stdProcs.textProc); /* draw one char */
}
} /* myStdText */
pascal void
stdTextInParts(lengthByQuickDrawAfterScaling, byteCount, textBuffer, numer, denom, stdTextProcPtr)
int lengthByQuickDrawAfterScaling;
int byteCount;
Ptr textBuffer;
Point numer, denom;
Ptr stdTextProcPtr;
{
int numberOfStringParts;
int charactersInAPart;
int remainingCharacters;
int i;
/* There is a bug/limitation in QD: the intermediate bitmap */
/* into which text is drawn cannot be wider than 2040 pixels */
/* (rowButes <= 255). This intermediate bitmap is probably */
/* on stack -- it is not needed for unscaled text without stylistic variations. */
numberOfStringParts = (lengthByQuickDrawAfterScaling/2000) + 1;
charactersInAPart = byteCount / numberOfStringParts;
remainingCharacters = byteCount % numberOfStringParts;
for (i=1;i<=numberOfStringParts;i++) {
CallPascal(
(i==numberOfStringParts) ? charactersInAPart+remainingCharacters : charactersInAPart,
textBuffer, numer, denom, stdTextProcPtr);
textBuffer += charactersInAPart;
} /* for */
} /* stdTextInParts */
pascal void
myStdComment(kind, dataSize, dataHandle)
int kind;
int dataSize;
htApplicationComment dataHandle;
{
ptXprintGlobals xPrintGlobals;
if ((kind != 100) || (dataSize != sizeof(tApplicationComment)))
return;
if ((*dataHandle)->applicationSignature != 's89^')
return;
if ((*dataHandle)->localKind != 0)
return;
/* Now it must be our comment, let's save the original length of next string: */
xPrintGlobals = GET_XPRINT_GLOBALS;
xPrintGlobals->originalStringLength =
(*dataHandle)->originalLengthOfNextTextInPixels;
} /* myStdComment */
pascal void
getPICTData(bufferPtr, bytesRequested)
Ptr bufferPtr;
int bytesRequested;
{
long count;
ptXprintGlobals xPrintGlobals;
xPrintGlobals = GET_XPRINT_GLOBALS;
count = bytesRequested;
/*** retCode = ***/ FSRead(xPrintGlobals->spoolFileRefNum, &count, bufferPtr);
/* if (retCode != noErr) fill buffer with 0xFF, or, end of PICT data */
} /* getPICTData */